/**
 *
 */
package info.angelloznicoud.ircdl.bean;

import info.angelloznicoud.ircdl.exception.EmptyDownloadDirectoryException;
import info.angelloznicoud.ircdl.exception.InvalidDownloadFileName;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

/**
 * Class managing files to download.
 *
 * @author ANGELLOZ-NICOUD Sébastien
 * @version 1.0
 */
public class FileManager {

	private static final Logger LOG = Logger.getLogger(FileManager.class);

	private Map<String, MessageFile> files;

	private File filesFolder = null;

	/**
	 * FileManager
	 *
	 * @param filesFolder - path to files that contains XDCC messages.
	 *
	 * @throws EmptyDownloadDirectoryException
	 * @throws IOException
	 */
	public FileManager(File filesFolder) throws EmptyDownloadDirectoryException, IOException {
		this.filesFolder = filesFolder;
		this.files       = new HashMap<String, MessageFile>();
		this.loadFiles();
		this.reprise();
	}

	/**
	 * Load the "download file" which contains all XDCC messages.
	 *
	 * @throws EmptyDownloadDirectoryException if no file was found.
	 */
	private void loadFiles() throws EmptyDownloadDirectoryException {
		LOG.info("[BEGIN] | Loading download files");

		String[] files = this.filesFolder.list(new FilenameFilter() {

			@Override
			public boolean accept(File file, String path) {
				return new File(file.getAbsolutePath() + File.separator + path).isFile()
						&& !path.startsWith(".")
						&& !path.endsWith(".encours")
						&& !path.endsWith("~");
			}
		});

		if (files.length > 0) {
			for (String fileName : files) {
				try {
					MessageFile downloadFile = new MessageFile(new File(this.filesFolder.getAbsolutePath() + File.separator + fileName));
					this.files.put(downloadFile.getShortFileName(), downloadFile);
				} catch (IOException e) {
					LOG.warn("File " + fileName + " is not added to FileManager.", e);
				} catch (InvalidDownloadFileName e) {
					LOG.warn("File " + fileName + " is not added to FileManager.", e);
				}
			}
		} else {
			throw new EmptyDownloadDirectoryException(this.filesFolder.getAbsolutePath());
		}

		LOG.info("[END] | Loading download files");
	}

	/**
	 * If program stopped abrutly when download was running,
	 * this method put the non finished download on top of download
	 * for each download file.
	 *
	 * @throws IOException if I/O errors occur.
	 */
	private void reprise() throws IOException {
		for (MessageFile df : this.files.values()) {
			if (df.getInProgressDownloadFile() != null && df.getInProgressDownloadFile().exists()) {
				BufferedReader reader = new BufferedReader(new FileReader(df.getInProgressDownloadFile()));
				String line = null;

				while ((line = reader.readLine()) != null) {
					df.addLigne(line, 0);
				}

				reader.close();

				df.getInProgressDownloadFile().delete();
			}
		}
	}

	/**
	 *
	 * @param message
	 */
	public void retryDownload(DownloadMessage message) {
		MessageFile mf = this.files.get(message.getAddress());

		if (mf != null) {
			mf.addLigne(message.getMessage(), 0);
			mf.deleteCurrendDownloadFile();
		}
	}

	/**
	 * Get the total number of file to download.
	 *
	 * @return the number of file to download.
	 */
	public int nbDownloads() {
		int r = 0;

		for (MessageFile df : this.files.values()) {
			r += df.getDownloadSize();
		}

		return r;
	}

	/**
	 * The file manager contains files to download.
	 *
	 * @return <code>true</code> if number of file to download is greater than zero,
	 * <code>false</code> otherwise.
	 */
	public boolean hasFilesToDowload() {
		return nbDownloads() != 0;
	}

	/**
	 * Get a list of the servers and channels.
	 *
	 * @return a string array of servers names and channels.
	 */
	public String[] getServersAndChannels() {
		return this.files.keySet().toArray(new String[this.files.keySet().size()]);
	}

	/**
	 * Return the next download.
	 *
	 * @return The next download informations as DownloadMessage object.
	 */
	public DownloadMessage getNextMessage(int maxDownload) {
		if (getNumberDownloadInProgress() < maxDownload) {
			for (MessageFile dlFile : this.files.values()) {
				if (!dlFile.downloadInProgress()) {
					String nextDlMsg = dlFile.getNextFileToDowload();
					if (nextDlMsg != null) {
						return new DownloadMessage(dlFile.getChannel(), dlFile.getServer(), nextDlMsg, this);
					}
				}
			}
		}

		return null;
	}

	/**
	 * Get the number of donwload in progress.
	 * @return
	 */
	public int getNumberDownloadInProgress() {
		int r = 0;

		for (MessageFile file: this.files.values()) {
			if (file.downloadInProgress()) {
				r++;
			}
		}

		return r;
	}

	/**
	 * Event called when a download is finished.
	 *
	 * @param dlMessage - the object message
	 */
	public void onDownloadFinished(DownloadMessage dlMessage) {
		MessageFile downloadFile = this.files.get(dlMessage.getAddress());
		downloadFile.deleteCurrendDownloadFile();
	}

	/**
	 * Get the next message for download from a particular server.
	 *
	 * @param server - the server name.
	 * @return the next download from the above server.
	 */
	public String getNextMessage(String server) {
		return null; //FIXME
	}

}
